
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: SidekarHandlerInterceptor.java 374 2010-03-10 20:43:19Z archie.cobbs $
 */

package org.dellroad.sidekar.spring;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.dellroad.sidekar.util.CloseableNotes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * A Spring {@link org.springframework.web.servlet.handler.HandlerInterceptor HandlerInterceptor} that wraps
 * the request inside a call to {@link SidekarEnvironment#performAction SidekarEnvironment.performAction()}
 * or {@link SidekarEnvironment#performTransaction SidekarEnvironment.performTransaction()}.
 * This allows the model and the view to execute all within a single (trans)action.
 *
 * <p>
 * By default, {@link SidekarEnvironment#performAction SidekarEnvironment.performAction()}
 * is used. Set the {@link #setTransactional transactional} property to {@code true} for
 * {@link SidekarEnvironment#performTransaction SidekarEnvironment.performTransaction()}.
 * </p>
 *
 * <p>
 * The {@link #setSidekarEnvironment sidekarEnvironment} property is required.
 * </p>
 */
public class SidekarHandlerInterceptor extends HandlerInterceptorAdapter {

    /**
     * The configured {@link SidekarEnvironment} from which transactions are created.
     */
    private SidekarEnvironment env;

    /**
     * Transaction configuration.
     */
    private TransactionConfig transactionConfig;

    /**
     * Whether we should operate transactionally.
     */
    private boolean transactional;

    /**
     * Get the {@link SidekarEnvironment} that this instance will operate on.
     */
    public SidekarEnvironment getSidekarEnvironment() {
        return this.env;
    }

    /**
     * Configure the {@link SidekarEnvironment} that this instance will operate on.
     */
    @Autowired
    public void setSidekarEnvironment(SidekarEnvironment env) {
        this.env = env;
    }

    /**
     * Get the configured {@link TransactionConfig}.
     */
    public TransactionConfig getTransactionConfig() {
        return this.transactionConfig;
    }

    /**
     * Configure the {@link TransactionConfig}.
     */
    public void setTransactionConfig(TransactionConfig transactionConfig) {
        this.transactionConfig = transactionConfig;
    }

    /**
     * Determine whether this instance is configured to operate transactionally. Default is {@code false}.
     */
    public boolean isTransactional() {
        return this.transactional;
    }

    /**
     * Configure whether to operate transactionally. Default is {@code false}.
     */
    public void setTransactional(boolean transactional) {
        this.transactional = transactional;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (this.transactional)
            this.env.pushTransaction(this.env.beginTransaction(null, this.transactionConfig));
        else
            CloseableNotes.push();
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      throws Exception {
        if (this.transactional)
            this.env.popTransaction(ex == null);
        else
            CloseableNotes.pop();
    }
}

